@phenix1024/editor 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +283 -2
- package/dist/ophx-editor.cjs +59 -0
- package/dist/ophx-editor.css +2 -0
- package/dist/ophx-editor.iife.js +207 -0
- package/dist/ophx-editor.iife.js.map +1 -0
- package/dist/ophx-editor.js +1360 -0
- package/dist/ophx-editor.standalone.js +21084 -0
- package/dist/ophx-editor.standalone.js.map +1 -0
- package/dist/types/editor/extensions.d.ts +30 -0
- package/dist/types/editor/ophx-archive.d.ts +27 -0
- package/dist/types/editor/ophx-editor.d.ts +70 -0
- package/dist/types/editor/ophx.css.d.ts +2 -0
- package/dist/types/editor/template-fields.d.ts +7 -0
- package/dist/types/editor/toolbar.d.ts +6 -0
- package/dist/types/index.d.ts +6 -0
- package/package.json +81 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mars102
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,284 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @phenix1024/editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Редактор документов для браузера на базе [Tiptap](https://tiptap.dev). Работает без фреймворков (подойдут React, Vue, Svelte, Angular или чистый JS), ему нужен только DOM-элемент. Документы сохраняются в переносимый архив `.ophx` (HTML + CSS + изображения в одном ZIP).
|
|
4
|
+
|
|
5
|
+
- Заголовки, списки, отступы, таблицы, изображения с обтеканием, ссылки и цветные метки.
|
|
6
|
+
- Markdown-подобный ввод: `# ` даёт заголовок, `- ` маркированный список, `1. ` нумерованный.
|
|
7
|
+
- Готовая панель инструментов: стиль текста, поиск с подсветкой, содержание, отмена и повтор.
|
|
8
|
+
- Режим просмотра, который запоминается между перезагрузками страницы.
|
|
9
|
+
- Поля шаблонов: заполняемые места в документе, сбор значений, проверка обязательных полей.
|
|
10
|
+
|
|
11
|
+
## Установка
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i @phenix1024/editor
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Быстрый старт
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<div id="toolbar"></div>
|
|
21
|
+
<div id="editor"></div>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { OphxEditor } from '@phenix1024/editor'
|
|
26
|
+
import '@phenix1024/editor/style.css'
|
|
27
|
+
|
|
28
|
+
const editor = new OphxEditor({
|
|
29
|
+
element: '#editor', // элемент или CSS-селектор
|
|
30
|
+
toolbar: '#toolbar', // необязательно: встроенная панель инструментов
|
|
31
|
+
content: '<h1>Документ</h1><p>Начните писать…</p>',
|
|
32
|
+
onUpdate: () => localStorage.setItem('draft', editor.getHTML()),
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Готово: редактор с панелью работает. Когда редактор больше не нужен, вызовите `editor.destroy()`. Он уберёт и панель.
|
|
37
|
+
|
|
38
|
+
## Частые задачи
|
|
39
|
+
|
|
40
|
+
### Получить и задать содержимое
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const html = editor.getHTML() // для автосохранения и отправки на сервер
|
|
44
|
+
const json = editor.getJSON() // структура документа Tiptap
|
|
45
|
+
const text = editor.getText() // простой текст
|
|
46
|
+
|
|
47
|
+
editor.setContent('<p>Новый текст</p>') // onUpdate не вызывается
|
|
48
|
+
editor.setContent('<p>Новый текст</p>', { emitUpdate: true })
|
|
49
|
+
editor.clearContent()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Режим просмотра
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
editor.setEditable(false) // только чтение
|
|
56
|
+
editor.setEditable(true) // снова редактирование
|
|
57
|
+
editor.isEditable // текущий режим
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Режим сохраняется в `localStorage` и восстанавливается после перезагрузки. В режиме просмотра панель отключает инструменты форматирования, а поиск и содержание остаются доступными.
|
|
61
|
+
|
|
62
|
+
| Опция | Поведение |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `editable: false` | Начальный режим, если сохранённого значения ещё нет. |
|
|
65
|
+
| `editableStorageKey: 'my-key'` | Свой ключ `localStorage`, например отдельный для каждого документа. |
|
|
66
|
+
| `editableStorageKey: null` | Не запоминать режим: всегда используется `editable`. |
|
|
67
|
+
|
|
68
|
+
### Сохранить и открыть `.ophx`
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// Скачать файл в браузере
|
|
72
|
+
await editor.downloadOphx({ filename: 'Договор', title: 'Договор поставки' })
|
|
73
|
+
|
|
74
|
+
// Или получить Blob, например чтобы отправить на сервер
|
|
75
|
+
const blob = await editor.saveOphx({ filename: 'Договор' })
|
|
76
|
+
|
|
77
|
+
// Открыть файл, выбранный пользователем
|
|
78
|
+
input.addEventListener('change', async () => {
|
|
79
|
+
const file = input.files?.[0]
|
|
80
|
+
if (!file) return
|
|
81
|
+
const doc = await editor.loadOphx(file) // { title, filename, styles, css, html, ... }
|
|
82
|
+
console.log(doc.title)
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
После `loadOphx()` имя, заголовок и стили архива запоминаются. Следующий `saveOphx()` или `downloadOphx()` без параметров сохранит документ с теми же метаданными. Параметры, переданные явно, имеют приоритет.
|
|
87
|
+
|
|
88
|
+
### Свои действия поверх Tiptap
|
|
89
|
+
|
|
90
|
+
`editor.editor` — это исходный экземпляр Tiptap `Editor`. Через него доступны все команды и события:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
editor.editor.chain().focus().toggleBold().run()
|
|
94
|
+
editor.editor.on('selectionUpdate', () => { /* ... */ })
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Дополнительные расширения Tiptap передаются через `extensions: [...]`.
|
|
98
|
+
|
|
99
|
+
### Загрузка изображений на сервер
|
|
100
|
+
|
|
101
|
+
По умолчанию панель вставляет изображения как `data:` URL, а при сохранении они упаковываются в `.ophx`. Чтобы загружать их на свой сервер:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const editor = new OphxEditor({
|
|
105
|
+
element: '#editor',
|
|
106
|
+
toolbar: '#toolbar',
|
|
107
|
+
toolbarOptions: {
|
|
108
|
+
onInsertImage: async (file) => {
|
|
109
|
+
const url = await uploadToServer(file)
|
|
110
|
+
editor.editor.chain().focus().setImage({ src: url, alt: file.name }).run()
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Метки
|
|
117
|
+
|
|
118
|
+
Выделите текст и нажмите «Метка» на панели. Во всплывающем окне введите название или выберите существующее. Клик по метке показывает её название, а в режиме редактирования там же её можно изменить или удалить. Метки сохраняются как `<mark data-label="...">`. Метки «важно» и «идея» имеют свои цвета, цвета остальных задаются в CSS:
|
|
119
|
+
|
|
120
|
+
```css
|
|
121
|
+
.ophx-editor-surface mark[data-label="срочно"] { background: #ffe0b3; }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Поля шаблонов
|
|
125
|
+
|
|
126
|
+
Поле — это элемент с классом `ophx-field`, атрибутом `contenteditable="true"` и уникальным `data-ophx-field`:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<p>Покупатель: <span class="ophx-field" contenteditable="true"
|
|
130
|
+
data-ophx-field="buyer" data-ophx-label="Покупатель" data-ophx-required="true"></span></p>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
editor.getFields() // [{ id, label, required, value }]
|
|
135
|
+
editor.getMissingRequiredFields() // незаполненные обязательные поля
|
|
136
|
+
|
|
137
|
+
import { convertUnderscoresToFields } from '@phenix1024/editor'
|
|
138
|
+
const { html } = convertUnderscoresToFields('<p>ФИО: ______</p>') // «___» превращается в поля f1, f2, …
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Фрагменты с `contenteditable="false"` сохраняются и остаются нередактируемыми.
|
|
142
|
+
|
|
143
|
+
## Фреймворки
|
|
144
|
+
|
|
145
|
+
### React
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useEffect, useRef } from 'react'
|
|
149
|
+
import { OphxEditor } from '@phenix1024/editor'
|
|
150
|
+
import '@phenix1024/editor/style.css'
|
|
151
|
+
|
|
152
|
+
export function DocumentEditor({ initialHtml }: { initialHtml?: string }) {
|
|
153
|
+
const editorRef = useRef<HTMLDivElement>(null)
|
|
154
|
+
const toolbarRef = useRef<HTMLDivElement>(null)
|
|
155
|
+
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
const editor = new OphxEditor({
|
|
158
|
+
element: editorRef.current!,
|
|
159
|
+
toolbar: toolbarRef.current!,
|
|
160
|
+
content: initialHtml,
|
|
161
|
+
})
|
|
162
|
+
return () => editor.destroy()
|
|
163
|
+
}, [])
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<>
|
|
167
|
+
<div ref={toolbarRef} />
|
|
168
|
+
<div ref={editorRef} />
|
|
169
|
+
</>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Vue 3
|
|
175
|
+
|
|
176
|
+
```vue
|
|
177
|
+
<script setup lang="ts">
|
|
178
|
+
import { onMounted, onBeforeUnmount, ref } from 'vue'
|
|
179
|
+
import { OphxEditor } from '@phenix1024/editor'
|
|
180
|
+
import '@phenix1024/editor/style.css'
|
|
181
|
+
|
|
182
|
+
const editorEl = ref<HTMLElement>()
|
|
183
|
+
const toolbarEl = ref<HTMLElement>()
|
|
184
|
+
let editor: OphxEditor | undefined
|
|
185
|
+
|
|
186
|
+
onMounted(() => {
|
|
187
|
+
editor = new OphxEditor({ element: editorEl.value!, toolbar: toolbarEl.value! })
|
|
188
|
+
})
|
|
189
|
+
onBeforeUnmount(() => editor?.destroy())
|
|
190
|
+
</script>
|
|
191
|
+
|
|
192
|
+
<template>
|
|
193
|
+
<div ref="toolbarEl" />
|
|
194
|
+
<div ref="editorEl" />
|
|
195
|
+
</template>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Не храните экземпляр `OphxEditor` в `ref()` или `reactive()`: прокси Vue мешают работе Tiptap.
|
|
199
|
+
|
|
200
|
+
### Без сборщика (CDN)
|
|
201
|
+
|
|
202
|
+
```html
|
|
203
|
+
<link rel="stylesheet" href="https://unpkg.com/@phenix1024/editor@0.2.0/dist/ophx-editor.css">
|
|
204
|
+
<script src="https://unpkg.com/@phenix1024/editor@0.2.0/dist/ophx-editor.iife.js"></script>
|
|
205
|
+
|
|
206
|
+
<div id="toolbar"></div>
|
|
207
|
+
<div id="editor"></div>
|
|
208
|
+
<script>
|
|
209
|
+
const editor = new OphxEditor.OphxEditor({ element: '#editor', toolbar: '#toolbar' })
|
|
210
|
+
</script>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Через jsDelivr: `https://cdn.jsdelivr.net/npm/@phenix1024/editor@0.2.0/dist/...`. Версию в ссылке лучше фиксировать.
|
|
214
|
+
|
|
215
|
+
## API
|
|
216
|
+
|
|
217
|
+
### `new OphxEditor(options)`
|
|
218
|
+
|
|
219
|
+
| Опция | Тип | По умолчанию | Описание |
|
|
220
|
+
| --- | --- | --- | --- |
|
|
221
|
+
| `element` | `HTMLElement \| string` | — | Контейнер документа (обязательно). |
|
|
222
|
+
| `toolbar` | `HTMLElement \| string` | — | Контейнер встроенной панели инструментов. |
|
|
223
|
+
| `toolbarOptions` | `{ onInsertImage?(file) }` | — | Настройки панели. |
|
|
224
|
+
| `content` | `string \| JSON` | `'<p></p>'` | Начальное содержимое. |
|
|
225
|
+
| `placeholder` | `string` | `'Начните писать…'` | Подсказка в пустом документе. |
|
|
226
|
+
| `editable` | `boolean` | `true` | Начальный режим, если нет сохранённого. |
|
|
227
|
+
| `editableStorageKey` | `string \| null` | `'ophx-editor-editable'` | Ключ `localStorage` для режима; `null` отключает хранение. |
|
|
228
|
+
| `onUpdate` | `({ editor }) => void` | — | Вызывается при каждом изменении документа. |
|
|
229
|
+
| `extensions` | `Extension[]` | `[]` | Дополнительные расширения Tiptap. |
|
|
230
|
+
|
|
231
|
+
### Методы и свойства
|
|
232
|
+
|
|
233
|
+
| | |
|
|
234
|
+
| --- | --- |
|
|
235
|
+
| `getHTML()`, `getJSON()`, `getText()` | Содержимое документа. |
|
|
236
|
+
| `setContent(content, { emitUpdate? })`, `clearContent()` | Замена содержимого. |
|
|
237
|
+
| `setEditable(bool)`, `isEditable` | Режим редактирования или просмотра. |
|
|
238
|
+
| `isEmpty` | Пустой ли документ. |
|
|
239
|
+
| `focus(pos?)`, `blur()` | Фокус: `'start'`, `'end'`, `'all'` или позиция. |
|
|
240
|
+
| `undo()`, `redo()`, `canUndo()`, `canRedo()` | История изменений. |
|
|
241
|
+
| `setSearchQuery(query)` | Подсветка найденного текста. |
|
|
242
|
+
| `loadOphx(file)` | Открыть `.ophx`, возвращает `OphxDocument`. |
|
|
243
|
+
| `saveOphx(options?)` | Создать `.ophx` и вернуть `Blob`. |
|
|
244
|
+
| `downloadOphx(options?)` | Скачать `.ophx`. |
|
|
245
|
+
| `getFields()`, `getMissingRequiredFields()` | Поля шаблона. |
|
|
246
|
+
| `destroy()` | Удалить редактор и панель. |
|
|
247
|
+
| `editor` | Исходный Tiptap `Editor`. |
|
|
248
|
+
|
|
249
|
+
Параметры сохранения (`OphxExportOptions`): `filename` (имя без `.ophx`), `title`, `styles` (`{ 'имя.css': 'css…' }`), `assets` (файлы изображений по имени), `bodyContentEditable`.
|
|
250
|
+
|
|
251
|
+
### Другие экспорты
|
|
252
|
+
|
|
253
|
+
- `mountToolbar(host, tiptapEditor, options?)` — смонтировать панель вручную. Возвращает функцию очистки.
|
|
254
|
+
- `exportOphx(html, options)`, `importOphx(file)`, `downloadBlob(blob, filename)` — работа с архивами без редактора.
|
|
255
|
+
- `convertUnderscoresToFields(html, prefix?)` — превращает `___` в поля шаблона.
|
|
256
|
+
- Расширения Tiptap: `Label`, `CollapsibleHeading`, `Indentation`, `OphxInlineField`, `OphxBlockField`, `OphxReadonlyBlock`, `OphxReadonlyInline`, `OphxEditability`, `TextSearch`, `WysiwymRules`.
|
|
257
|
+
|
|
258
|
+
## Формат `.ophx`
|
|
259
|
+
|
|
260
|
+
Обычный ZIP, все файлы лежат в корне. Внутри ровно один HTML, его имя совпадает с именем архива. Рядом лежат CSS и изображения:
|
|
261
|
+
|
|
262
|
+
```text
|
|
263
|
+
Договор.ophx
|
|
264
|
+
├── Договор.html
|
|
265
|
+
├── default.css
|
|
266
|
+
└── image-1.png
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- Вставленные изображения (`data:`, `blob:`) упаковываются как `image-<n>.<ext>`. Внешние `http(s)` ссылки остаются ссылками.
|
|
270
|
+
- При открытии из HTML удаляются `<script>` и обработчики событий (`on*`).
|
|
271
|
+
- CSS из архива возвращается в `doc.styles` и к странице автоматически не применяется. Где его применять, решает приложение.
|
|
272
|
+
|
|
273
|
+
## Разработка
|
|
274
|
+
|
|
275
|
+
```sh
|
|
276
|
+
npm install
|
|
277
|
+
npm run dev # демо на http://localhost:5173
|
|
278
|
+
npm run typecheck
|
|
279
|
+
npm run build # демо в dist-demo/, библиотека в dist/
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Лицензия
|
|
283
|
+
|
|
284
|
+
MIT
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));let c=require("@tiptap/core"),l=require("@tiptap/starter-kit");l=s(l,1);let u=require("@tiptap/extension-underline");u=s(u,1);let d=require("@tiptap/extension-link");d=s(d,1);let f=require("@tiptap/extension-placeholder");f=s(f,1);let p=require("@tiptap/extension-table"),m=require("@tiptap/extension-table-row"),h=require("@tiptap/extension-table-header"),g=require("@tiptap/extension-table-cell"),_=require("jszip");_=s(_,1);let v=require("@tiptap/extension-heading");v=s(v,1);let y=require("@tiptap/extension-image");y=s(y,1);let b=require("@tiptap/pm/state"),x=require("@tiptap/pm/view");var ee=[{action:`bold`,label:`Ж`,title:`Жирный (⌘/Ctrl+B)`},{action:`italic`,label:`К`,title:`Курсив (⌘/Ctrl+I)`},{action:`underline`,label:`Ч`,title:`Подчёркнутый (⌘/Ctrl+U)`},{action:`strike`,label:`З`,title:`Зачёркнутый`},{action:`bullet`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="7" cy="6" r="1.3"/><circle cx="7" cy="12" r="1.3"/><circle cx="7" cy="18" r="1.3"/><path d="M11 6h8M11 12h8M11 18h8"/></svg>`,title:`Маркированный список`},{action:`ordered`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M10 6h10M10 12h10M10 18h10"/><path d="M4 4.8 5.2 4v4.2M3.8 8.2h2.8M3.8 10.8c.2-.5.6-.8 1.2-.8.8 0 1.2.4 1.2 1 0 .4-.3.8-.7 1.2l-1.6 1.5h2.5M3.8 16.2c.2-.5.6-.8 1.2-.8.8 0 1.2.4 1.2.9 0 .5-.4.8-1 .8.7 0 1.1.3 1.1.9 0 .6-.5 1-1.3 1-.6 0-1-.2-1.3-.7"/></svg>`,title:`Нумерованный список`},{action:`outdent`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 6h14M5 12h10M5 18h14"/><path d="M9 9l-4 3 4 3"/></svg>`,title:`Уменьшить отступ`},{action:`indent`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M5 6h14M9 12h10M5 18h14"/><path d="M15 9l4 3-4 3"/></svg>`,title:`Увеличить отступ`},{action:`link`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M10 13.5a4.5 4.5 0 0 0 6.4 0l3-3a4.5 4.5 0 0 0-6.4-6.4l-1.7 1.7"/><path d="M14 10.5a4.5 4.5 0 0 0-6.4 0l-3 3A4.5 4.5 0 0 0 11 19.9l1.7-1.7"/></svg>`,title:`Добавить ссылку`},{action:`image`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9" r="1.5"/><path d="m21 15-5-5L5 20"/></svg>`,title:`Вставить изображение`},{action:`table`,label:`<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="3" y="4" width="18" height="16" rx="1.5"/><path d="M3 10h18M9 4v16M15 4v16"/></svg>`,title:`Вставить таблицу`},{action:`label`,label:`Метка`,title:`Выделить текст меткой`},{action:`toc`,label:`Содержание`,title:`Содержание документа`},{action:`undo`,label:`↶`,title:`Отменить (⌘/Ctrl+Z)`},{action:`redo`,label:`↷`,title:`Повторить (⌘/Ctrl+Shift+Z)`}];function te(e,t,n={}){e.classList.add(`ophx-toolbar`),e.replaceChildren();let r=(e,t)=>{e.style.display=t?``:`none`,e.hidden=!t},i=()=>{f=!1,r(l,!1),o.setAttribute(`aria-expanded`,`false`),r(F,!1),h.get(`toc`)?.setAttribute(`aria-expanded`,`false`)},a=document.createElement(`div`);a.className=`ophx-toolbar__style-wrap`;let o=document.createElement(`button`);o.type=`button`,o.className=`ophx-toolbar__style`,o.setAttribute(`aria-label`,`Стиль текста`),o.setAttribute(`aria-haspopup`,`listbox`),o.setAttribute(`aria-expanded`,`false`);let s=document.createElement(`span`);s.className=`ophx-toolbar__style-label`,o.append(s);let l=document.createElement(`div`);l.className=`ophx-toolbar__style-menu`,r(l,!1);let u=[{value:`p`,label:`Обычный текст`,className:`ophx-toolbar__style-preview--p`},{value:`1`,label:`Заголовок 1`,className:`ophx-toolbar__style-preview--h1`},{value:`2`,label:`Заголовок 2`,className:`ophx-toolbar__style-preview--h2`},{value:`3`,label:`Заголовок 3`,className:`ophx-toolbar__style-preview--h3`}],d=e=>{let n=Number(e),r=t.chain().focus();e===`p`?r.setParagraph().run():r.setHeading({level:n}).run(),m()};for(let e of u){let t=document.createElement(`button`);t.type=`button`,t.className=`ophx-toolbar__style-option`,t.dataset.value=e.value,t.setAttribute(`role`,`option`),t.setAttribute(`aria-selected`,`false`),t.innerHTML=`<span class="ophx-toolbar__style-preview ${e.className}">${e.label}</span>`,t.addEventListener(`mousedown`,e=>{e.preventDefault(),e.stopPropagation()}),t.addEventListener(`click`,()=>d(e.value)),l.append(t)}let f=!1,p=()=>{f=!0,r(l,!0),o.setAttribute(`aria-expanded`,`true`)},m=()=>{f=!1,r(l,!1),o.setAttribute(`aria-expanded`,`false`)};o.addEventListener(`click`,e=>{e.stopPropagation(),f?m():(r(F,!1),p())}),o.addEventListener(`keydown`,e=>{e.key===`Escape`&&m()}),document.addEventListener(`click`,e=>{let t=e.target,n=a.contains(t),r=F.contains(t);!n&&!r&&i()}),a.append(o,l),e.append(a);let h=new Map;for(let n of ee){let r=document.createElement(`button`);r.type=`button`,r.className=`ophx-toolbar__button`,r.dataset.action=n.action,r.innerHTML=n.label,r.title=n.title,r.setAttribute(`aria-label`,n.title),r.addEventListener(`mousedown`,e=>e.preventDefault()),r.addEventListener(`click`,e=>{n.action===`toc`?e.stopPropagation():n.action===`link`?G():n.action===`table`?oe():n.action===`label`?(e.stopPropagation(),Te()):t.isEditable&&ae(n.action,t,_,F)}),h.set(n.action,r),e.append(r)}let g=document.createElement(`input`);g.type=`search`,g.className=`ophx-toolbar__search`,g.placeholder=`Поиск…`,g.setAttribute(`aria-label`,`Поиск по тексту`),g.addEventListener(`input`,()=>t.commands.setSearchQuery(g.value)),g.addEventListener(`keydown`,e=>{e.key===`Escape`&&(g.value=``,t.commands.setSearchQuery(``),t.commands.focus())}),e.append(g);let _=document.createElement(`input`);_.type=`file`,_.accept=`image/*`,_.hidden=!0,_.addEventListener(`change`,()=>{let e=_.files?.[0];if(e){if(n.onInsertImage)n.onInsertImage(e);else{let n=new FileReader;n.addEventListener(`load`,()=>{typeof n.result==`string`&&t.chain().focus().setImage({src:n.result,alt:e.name}).run()}),n.readAsDataURL(e)}}_.value=``}),e.append(_);let v=document.createElement(`form`);v.className=`ophx-table-popover`,v.setAttribute(`aria-label`,`Создать таблицу`),v.hidden=!0,v.innerHTML=`
|
|
2
|
+
<label class="ophx-table-popover__label">Строки<input name="rows" type="number" min="1" max="30" value="3" required></label>
|
|
3
|
+
<label class="ophx-table-popover__label">Столбцы<input name="cols" type="number" min="1" max="20" value="3" required></label>
|
|
4
|
+
<label class="ophx-table-popover__header"><input name="header" type="checkbox" checked> Заголовок таблицы</label>
|
|
5
|
+
<button class="ophx-table-popover__insert" type="submit">Вставить таблицу</button>
|
|
6
|
+
`,document.body.append(v);let y=h.get(`table`),b=v.querySelector(`input[name="rows"]`),x=v.querySelector(`input[name="cols"]`),te=v.querySelector(`input[name="header"]`),S=()=>{if(v.hidden)return;let e=y.getBoundingClientRect(),t=v.offsetWidth,n=Math.max(8,Math.min(e.left,window.innerWidth-t-8));v.style.left=`${n}px`,v.style.top=`${Math.min(e.bottom+8,window.innerHeight-v.offsetHeight-8)}px`};function C(){v.hidden=!0,y.setAttribute(`aria-expanded`,`false`),window.removeEventListener(`resize`,S),document.removeEventListener(`scroll`,S,!0)}function oe(){t.isEditable&&(v.hidden?(v.hidden=!1,y.setAttribute(`aria-expanded`,`true`),S(),window.addEventListener(`resize`,S),document.addEventListener(`scroll`,S,!0),b.focus(),b.select()):C())}y.setAttribute(`aria-haspopup`,`dialog`),y.setAttribute(`aria-expanded`,`false`),v.addEventListener(`mousedown`,e=>e.stopPropagation()),v.addEventListener(`submit`,e=>{e.preventDefault();let n=Math.max(1,Math.min(30,Number(b.value)||1)),r=Math.max(1,Math.min(20,Number(x.value)||1));b.value=String(n),x.value=String(r),t.chain().focus().insertTable({rows:n,cols:r,withHeaderRow:te.checked}).run(),C()}),v.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),C(),y.focus())});let w=e=>{let t=e.target;!v.contains(t)&&!y.contains(t)&&C()};document.addEventListener(`click`,w);let T=document.createElement(`div`);T.className=`ophx-table-context`,T.setAttribute(`role`,`toolbar`),T.setAttribute(`aria-label`,`Действия с таблицей`),T.hidden=!0,T.innerHTML=`
|
|
7
|
+
<button type="button" data-command="addRow" aria-label="Добавить строку ниже" title="Добавить строку ниже"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4h16v12H4zM4 10h16M10 4v12M12 20v-4M10 18h4"/></svg></button>
|
|
8
|
+
<button type="button" data-command="deleteRow" aria-label="Удалить строку" title="Удалить строку"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4h16v12H4zM4 10h16M10 4v12M10 20h4"/></svg></button>
|
|
9
|
+
<span class="ophx-table-context__divider" aria-hidden="true"></span>
|
|
10
|
+
<button type="button" data-command="addColumn" aria-label="Добавить столбец справа" title="Добавить столбец справа"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4h12v16H4zM10 4v16M20 12h-4M18 10v4"/></svg></button>
|
|
11
|
+
<button type="button" data-command="deleteColumn" aria-label="Удалить столбец" title="Удалить столбец"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 4h12v16H4zM10 4v16M18 10v4"/></svg></button>
|
|
12
|
+
`,document.body.append(T);let se=()=>{let{$from:e}=t.state.selection;for(let n=e.depth;n>0;--n){if(e.node(n).type.name!==`table`)continue;let r=t.view.nodeDOM(e.before(n));return r instanceof HTMLTableElement?r:r instanceof HTMLElement?r.querySelector(`table`):null}return null},E=()=>{if(T.hidden)return;let e=se();if(!e)return;let t=e.getBoundingClientRect(),n=T.offsetWidth,r=T.offsetHeight,i=Math.max(8,Math.min(t.left,window.innerWidth-n-8)),a=t.top-r-8>=8?t.top-r-8:Math.min(t.bottom+8,window.innerHeight-r-8);T.style.left=`${i}px`,T.style.top=`${Math.max(8,a)}px`},ce=()=>{!t.isEditable||!t.isActive(`table`)?(T.hidden=!0,window.removeEventListener(`resize`,E),document.removeEventListener(`scroll`,E,!0)):(T.hidden=!1,E(),window.addEventListener(`resize`,E),document.addEventListener(`scroll`,E,!0))};T.addEventListener(`mousedown`,e=>e.preventDefault()),T.addEventListener(`click`,e=>{let n=e.target.closest(`[data-command]`);if(!n)return;let r=n.dataset.command,i=t.chain().focus();r===`addRow`?i.addRowAfter().run():r===`deleteRow`?i.deleteRow().run():r===`addColumn`?i.addColumnAfter().run():r===`deleteColumn`&&i.deleteColumn().run()});let D=e=>{if(T.hidden)return;let t=e.target,n=se();!T.contains(t)&&!n?.contains(t)&&(T.hidden=!0)};document.addEventListener(`click`,D);let O=document.createElement(`div`);O.className=`ophx-image-popover`,O.setAttribute(`role`,`dialog`),O.setAttribute(`aria-label`,`Размер и обтекание изображения`),O.hidden=!0,O.innerHTML=`
|
|
13
|
+
<span class="ophx-image-popover__arrow" aria-hidden="true"></span>
|
|
14
|
+
<div class="ophx-image-popover__heading">Обтекание текстом</div>
|
|
15
|
+
<div class="ophx-image-popover__choices" role="group" aria-label="Обтекание текстом">
|
|
16
|
+
<button class="ophx-image-popover__choice" type="button" data-wrap="none" aria-label="Без обтекания" title="Без обтекания"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="8" height="7" rx="1"/><path d="M14 5h7M14 8h7M3 14h18M3 18h18"/></svg></button>
|
|
17
|
+
<button class="ophx-image-popover__choice" type="button" data-wrap="left" aria-label="Изображение слева, текст справа" title="Изображение слева, текст справа"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="4" width="8" height="10" rx="1"/><path d="M14 5h7M14 8h7M14 11h7M3 18h18"/></svg></button>
|
|
18
|
+
<button class="ophx-image-popover__choice" type="button" data-wrap="right" aria-label="Изображение справа, текст слева" title="Изображение справа, текст слева"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="13" y="4" width="8" height="10" rx="1"/><path d="M3 5h7M3 8h7M3 11h7M3 18h18"/></svg></button>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="ophx-image-popover__heading">Выравнивание</div>
|
|
21
|
+
<div class="ophx-image-popover__choices" role="group" aria-label="Выравнивание изображения">
|
|
22
|
+
<button class="ophx-image-popover__choice" type="button" data-align="left" aria-label="Прижать влево" title="Прижать влево"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 3v18M8 6h12v5H8zM8 15h12"/></svg></button>
|
|
23
|
+
<button class="ophx-image-popover__choice" type="button" data-align="center" aria-label="По центру" title="По центру"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v18M4 6h16v5H4zM4 15h16"/></svg></button>
|
|
24
|
+
<button class="ophx-image-popover__choice" type="button" data-align="right" aria-label="Прижать вправо" title="Прижать вправо"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 3v18M4 6h12v5H4zM4 15h12"/></svg></button>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="ophx-image-popover__size" aria-live="polite"></div>
|
|
27
|
+
`,document.body.append(O);let le=O.querySelector(`.ophx-image-popover__size`),k=null,A=()=>{if(!t.isActive(`image`))return null;let e=t.view.nodeDOM(t.state.selection.from);return e instanceof HTMLElement?e.querySelector(`img`):null},j=()=>{let e=A();if(O.hidden||!e)return;let t=e.getBoundingClientRect(),n=t.left+t.width/2,r=O.offsetWidth,i=O.offsetHeight,a=Math.max(8,Math.min(n-r/2,window.innerWidth-r-8)),o=t.bottom+i+12<=window.innerHeight-8;O.style.left=`${a}px`,O.style.top=`${o?t.bottom+10:Math.max(8,t.top-i-10)}px`,O.dataset.placement=o?`bottom`:`top`,O.style.setProperty(`--ophx-image-arrow-left`,`${Math.max(14,Math.min(n-a,r-14))}px`)};function M(){O.hidden=!0,window.removeEventListener(`resize`,j),document.removeEventListener(`scroll`,j,!0)}function ue(){if(!t.isEditable||!O.hidden)return;let e=A();e&&(k=t.state.selection.from,O.hidden=!1,de(),j(),e.complete||e.addEventListener(`load`,()=>{de(),j()},{once:!0}),window.addEventListener(`resize`,j),document.addEventListener(`scroll`,j,!0))}let N=e=>{k!==null&&t.chain().focus().setNodeSelection(k).updateAttributes(`image`,e).run()},de=()=>{if(O.hidden||!t.isActive(`image`))return;let e=t.getAttributes(`image`),n=A();n&&(le.textContent=`${Math.round(n.getBoundingClientRect().width)} px · потяните за боковой маркер, чтобы изменить размер`);for(let t of O.querySelectorAll(`[data-wrap]`)){let n=t.dataset.wrap===(e.textWrap??`none`);t.classList.toggle(`is-selected`,n),t.setAttribute(`aria-pressed`,String(n))}for(let t of O.querySelectorAll(`[data-align]`)){let n=t.dataset.align===(e.alignment??`left`);t.classList.toggle(`is-selected`,n),t.setAttribute(`aria-pressed`,String(n))}};O.addEventListener(`mousedown`,e=>e.stopPropagation()),O.addEventListener(`click`,e=>{let t=e.target.closest(`.ophx-image-popover__choice`);t&&((t.dataset.wrap===`none`||t.dataset.wrap===`left`||t.dataset.wrap===`right`)&&N({textWrap:t.dataset.wrap}),(t.dataset.align===`left`||t.dataset.align===`center`||t.dataset.align===`right`)&&N({alignment:t.dataset.align,textWrap:`none`}))}),O.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),M())});let P=e=>{let n=e.target,r=e.target.closest?.(`.ophx-image-node`),i=!!(r&&t.view.dom.contains(r));!O.contains(n)&&!i&&M()};document.addEventListener(`click`,P);let F=document.createElement(`nav`);F.className=`ophx-toc`,F.setAttribute(`aria-label`,`Содержание документа`),r(F,!1),e.append(F);let I=h.get(`toc`);I&&(I.setAttribute(`aria-haspopup`,`true`),I.setAttribute(`aria-expanded`,`false`),I.addEventListener(`click`,()=>{F.hidden||F.style.display===`none`?(m(),r(F,!0),I.setAttribute(`aria-expanded`,`true`)):i()}));let L=document.createElement(`form`);L.className=`ophx-link-popover`,L.setAttribute(`role`,`dialog`),L.setAttribute(`aria-label`,`Добавить ссылку`),L.hidden=!0,L.innerHTML=`
|
|
28
|
+
<span class="ophx-link-popover__arrow" aria-hidden="true"></span>
|
|
29
|
+
<label class="ophx-link-popover__label" for="ophx-link-url">Адрес ссылки</label>
|
|
30
|
+
<div class="ophx-link-popover__row">
|
|
31
|
+
<input class="ophx-link-popover__input" id="ophx-link-url" type="url" placeholder="https://example.com" autocomplete="url">
|
|
32
|
+
</div>
|
|
33
|
+
<div class="ophx-link-popover__actions">
|
|
34
|
+
<button class="ophx-link-popover__go" type="button">Перейти</button>
|
|
35
|
+
<button class="ophx-link-popover__remove" type="button">Удалить ссылку</button>
|
|
36
|
+
<button class="ophx-link-popover__submit" type="submit">ОК</button>
|
|
37
|
+
</div>
|
|
38
|
+
`,document.body.append(L);let R=L.querySelector(`.ophx-link-popover__input`),z=L.querySelector(`.ophx-link-popover__go`),B=L.querySelector(`.ophx-link-popover__remove`),V=h.get(`link`),H=null,U=()=>{if(!L.hidden&&H)try{let e=t.view.coordsAtPos(H.from),n=t.view.coordsAtPos(H.to),r=(e.left+n.right)/2,i=L.offsetWidth,a=L.offsetHeight,o=Math.max(8,Math.min(r-i/2,window.innerWidth-i-8)),s=n.bottom+a+14<=window.innerHeight-8,c=s?n.bottom+12:Math.max(8,e.top-a-12);L.style.left=`${o}px`,L.style.top=`${c}px`,L.dataset.placement=s?`bottom`:`top`,L.style.setProperty(`--ophx-link-arrow-left`,`${Math.max(14,Math.min(r-o,i-14))}px`)}catch{W()}};function W(){L.hidden=!0,V.setAttribute(`aria-expanded`,`false`),window.removeEventListener(`resize`,U),document.removeEventListener(`scroll`,U,!0)}function G(e=!0){if(!t.isEditable)return;if(!L.hidden&&e){W();return}t.state.selection.empty&&t.isActive(`link`)&&t.chain().extendMarkRange(`link`).run();let n=t.state.selection;n.empty||(H={from:n.from,to:n.to},R.value=t.getAttributes(`link`).href??``,B.hidden=!t.isActive(`link`),z.hidden=!R.value,L.hidden=!1,V.setAttribute(`aria-expanded`,`true`),U(),window.addEventListener(`resize`,U),document.addEventListener(`scroll`,U,!0),R.focus(),R.select())}V.setAttribute(`aria-haspopup`,`dialog`),V.setAttribute(`aria-expanded`,`false`);let K=e=>{let n=e.target.closest(`a[href]`);n&&t.view.dom.contains(n)&&(n.title=`⌘/Ctrl+клик — открыть ссылку; клик — выделить для редактирования через кнопку «Ссылка»`)},fe=e=>{let n=e.target.closest(`a[href]`);if(!n||!t.view.dom.contains(n))return;if(e.preventDefault(),e.stopPropagation(),e.metaKey||e.ctrlKey){re(n.href);return}let r=t.view.posAtCoords({left:e.clientX,top:e.clientY});r&&(t.chain().focus().setTextSelection(r.pos).extendMarkRange(`link`).run(),G(!1))};t.view.dom.addEventListener(`pointerover`,K),t.view.dom.addEventListener(`click`,fe),L.addEventListener(`mousedown`,e=>e.stopPropagation()),L.addEventListener(`submit`,e=>{if(e.preventDefault(),!H)return;let n=R.value.trim();n?(t.chain().focus().setTextSelection(H).setLink({href:n}).run(),W()):R.focus()}),z.addEventListener(`click`,()=>re(R.value)),R.addEventListener(`input`,()=>{z.hidden=!R.value.trim()}),B.addEventListener(`click`,()=>{H&&(t.chain().focus().setTextSelection(H).extendMarkRange(`link`).unsetLink().run(),W())}),L.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),W(),V.focus())});let pe=e=>{let t=e.target;!L.contains(t)&&!V.contains(t)&&W()};document.addEventListener(`click`,pe);let me=[`важно`,`идея`],q=document.createElement(`form`);q.className=`ophx-link-popover ophx-label-popover`,q.setAttribute(`role`,`dialog`),q.hidden=!0,q.innerHTML=`
|
|
39
|
+
<span class="ophx-link-popover__arrow" aria-hidden="true"></span>
|
|
40
|
+
<div class="ophx-label-popover__view">
|
|
41
|
+
<span class="ophx-link-popover__label">Метка</span>
|
|
42
|
+
<div class="ophx-label-popover__name"><mark class="ophx-label-popover__chip"></mark></div>
|
|
43
|
+
<div class="ophx-link-popover__actions ophx-label-popover__view-actions">
|
|
44
|
+
<button class="ophx-link-popover__remove" type="button" data-label-remove>Удалить метку</button>
|
|
45
|
+
<button class="ophx-link-popover__submit" type="button" data-label-edit>Изменить</button>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="ophx-label-popover__edit">
|
|
49
|
+
<label class="ophx-link-popover__label" for="ophx-label-name">Название метки</label>
|
|
50
|
+
<div class="ophx-link-popover__row">
|
|
51
|
+
<input class="ophx-link-popover__input" id="ophx-label-name" type="text" placeholder="Например, важно" autocomplete="off" maxlength="60">
|
|
52
|
+
</div>
|
|
53
|
+
<div class="ophx-label-popover__presets" role="group" aria-label="Быстрый выбор метки"></div>
|
|
54
|
+
<div class="ophx-link-popover__actions">
|
|
55
|
+
<button class="ophx-link-popover__remove" type="button" data-label-remove>Удалить метку</button>
|
|
56
|
+
<button class="ophx-link-popover__submit" type="submit">ОК</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
`,document.body.append(q);let J=q.querySelector(`#ophx-label-name`),he=q.querySelector(`.ophx-label-popover__chip`),ge=q.querySelector(`.ophx-label-popover__presets`),_e=q.querySelector(`[data-label-edit]`),ve=q.querySelectorAll(`[data-label-remove]`),ye=q.querySelector(`.ophx-label-popover__view-actions`),Y=h.get(`label`),X=null,be=`edit`,xe=!1,Z=()=>{!q.hidden&&X&&(ne(t,q,X)||Q())};function Q(){q.hidden=!0,Y.setAttribute(`aria-expanded`,`false`),window.removeEventListener(`resize`,Z),document.removeEventListener(`scroll`,Z,!0)}function Se(){let e=new Set(me);return t.state.doc.descendants(t=>{for(let n of t.marks)n.type.name===`label`&&n.attrs.label&&e.add(String(n.attrs.label))}),[...e]}function Ce(e){ge.replaceChildren();for(let t of Se()){let n=document.createElement(`button`);n.type=`button`,n.className=`ophx-label-popover__preset`,n.classList.toggle(`is-selected`,t===e);let r=document.createElement(`mark`);r.dataset.label=t,r.textContent=t,n.append(r),n.addEventListener(`click`,()=>Ee(t)),ge.append(n)}}function we(e,n,r){X=e,be=r,q.dataset.mode=r,q.setAttribute(`aria-label`,r===`view`?`Метка «${n}»`:`Установить метку`),he.textContent=n,he.dataset.label=n,ye.hidden=!t.isEditable,ve.forEach(e=>{e.hidden=!xe}),J.value=n,Ce(n),q.hidden=!1,Y.setAttribute(`aria-expanded`,String(r===`edit`)),Z(),window.addEventListener(`resize`,Z),document.addEventListener(`scroll`,Z,!0),r===`edit`&&(J.focus(),J.select())}function Te(){if(!t.isEditable)return;if(!q.hidden&&be===`edit`){Q();return}t.state.selection.empty&&t.isActive(`label`)&&t.chain().extendMarkRange(`label`).run();let{from:e,to:n,empty:r}=t.state.selection;if(r)return;xe=t.isActive(`label`);let i=xe?String(t.getAttributes(`label`).label??``):``;we({from:e,to:n},i,`edit`)}function Ee(e){let n=e.trim();X&&(n?(t.chain().focus().setTextSelection(X).setMark(`label`,{label:n}).run(),Q()):J.focus())}Y.setAttribute(`aria-haspopup`,`dialog`),Y.setAttribute(`aria-expanded`,`false`);let De=e=>{let n=e.target,r=n.closest(`mark`);if(!r||!t.view.dom.contains(r)||n.closest(`a[href]`))return null;let i=t.view.posAtCoords({left:e.clientX,top:e.clientY}),a=t.schema.marks.label;if(!i||!a)return null;let o=t.state.doc.resolve(i.pos),s=(0,c.getMarkRange)(o,a)??(i.pos>0?(0,c.getMarkRange)(t.state.doc.resolve(i.pos-1),a):void 0);if(!s)return null;let l=r.dataset.label??``;return t.state.doc.nodesBetween(s.from,s.to,e=>{let t=e.marks.find(e=>e.type===a);t&&(l=String(t.attrs.label??l))}),{range:s,name:l}},Oe=e=>{let n=e.target.closest(`mark[data-label]`);n&&t.view.dom.contains(n)&&(n.title=`Метка: ${n.dataset.label}`)},ke=e=>{if(e.metaKey||e.ctrlKey||e.shiftKey)return;let n=De(e);n&&(t.state.selection.empty||!t.isEditable)&&(e.stopPropagation(),xe=!0,we(n.range,n.name,`view`))};t.view.dom.addEventListener(`pointerover`,Oe),t.view.dom.addEventListener(`click`,ke),q.addEventListener(`mousedown`,e=>e.stopPropagation()),q.addEventListener(`submit`,e=>{e.preventDefault(),Ee(J.value)}),_e.addEventListener(`click`,()=>{X&&t.isEditable&&we(X,he.textContent??``,`edit`)}),ve.forEach(e=>e.addEventListener(`click`,()=>{X&&(t.chain().focus().setTextSelection(X).unsetMark(`label`).run(),Q())})),q.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),Q(),t.commands.focus())});let Ae=e=>{let t=e.target;!q.contains(t)&&!Y.contains(t)&&Q()};document.addEventListener(`click`,Ae);let $=()=>{let e=t.isEditable;o.disabled=!e;for(let[t,n]of h)t!==`toc`&&(n.disabled=!e);h.get(`undo`).disabled=!e||!t.can().undo(),h.get(`redo`).disabled=!e||!t.can().redo(),ce(),!e||!t.isActive(`image`)?M():O.hidden?ue():(de(),j()),h.get(`bold`).classList.toggle(`is-active`,t.isActive(`bold`)),h.get(`italic`).classList.toggle(`is-active`,t.isActive(`italic`)),h.get(`underline`).classList.toggle(`is-active`,t.isActive(`underline`)),h.get(`strike`).classList.toggle(`is-active`,t.isActive(`strike`)),h.get(`label`).classList.toggle(`is-active`,t.isActive(`label`)),e||W(),!e&&be===`edit`?Q():q.hidden||Z();let n=t.getAttributes(`heading`).level,r=n?String(n):(t.isActive(`paragraph`),`p`),i=n?`Заголовок ${n}`:`Обычный текст`;s.textContent=i;for(let e of l.querySelectorAll(`.ophx-toolbar__style-option`)){let t=e.dataset.value===r;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`is-selected`,t)}F.replaceChildren(),t.state.doc.descendants((e,n)=>{if(e.type.name!==`heading`)return;let r=document.createElement(`button`);r.type=`button`,r.className=`ophx-toc__item`,r.style.paddingInlineStart=`${Math.max(0,Number(e.attrs.level)-1)*12}px`,r.textContent=e.textContent||`Без названия`,r.addEventListener(`mousedown`,e=>e.preventDefault()),r.addEventListener(`click`,()=>{t.commands.setTextSelection(n+1),t.commands.focus(),t.commands.scrollIntoView()}),F.append(r)})},je=({transaction:e})=>{if($(),!e.docChanged)return;let n=ie(e.before,e.doc);n!==null&&t.commands.setNodeSelection(n)},Me=()=>$();return t.on(`transaction`,je),t.on(`selectionUpdate`,Me),t.view.dom.addEventListener(`ophx:editablechange`,$),$(),()=>{t.off(`transaction`,je),t.off(`selectionUpdate`,Me),t.view.dom.removeEventListener(`ophx:editablechange`,$),document.removeEventListener(`click`,w),C(),v.remove(),document.removeEventListener(`click`,D),window.removeEventListener(`resize`,E),document.removeEventListener(`scroll`,E,!0),T.remove(),document.removeEventListener(`click`,P),M(),O.remove(),t.view.dom.removeEventListener(`pointerover`,K),t.view.dom.removeEventListener(`click`,fe),document.removeEventListener(`click`,pe),W(),L.remove(),t.view.dom.removeEventListener(`pointerover`,Oe),t.view.dom.removeEventListener(`click`,ke),document.removeEventListener(`click`,Ae),Q(),q.remove(),e.replaceChildren(),e.classList.remove(`ophx-toolbar`)}}function ne(e,t,n){try{let r=e.view.coordsAtPos(n.from),i=e.view.coordsAtPos(n.to),a=(r.left+i.right)/2,o=t.offsetWidth,s=t.offsetHeight,c=Math.max(8,Math.min(a-o/2,window.innerWidth-o-8)),l=i.bottom+s+14<=window.innerHeight-8,u=l?i.bottom+12:Math.max(8,r.top-s-12);return t.style.left=`${c}px`,t.style.top=`${u}px`,t.dataset.placement=l?`bottom`:`top`,t.style.setProperty(`--ophx-link-arrow-left`,`${Math.max(14,Math.min(a-c,o-14))}px`),!0}catch{return!1}}function re(e){try{let t=new URL(e.trim(),window.location.href);[`http:`,`https:`,`mailto:`].includes(t.protocol)&&window.open(t.href,`_blank`,`noopener,noreferrer`)}catch{}}function ie(e,t){let n=new Map;e.descendants(e=>{if(e.type.name!==`image`)return;let t=String(e.attrs.src??``);n.set(t,(n.get(t)??0)+1)});let r=null;return t.descendants((e,t)=>{if(e.type.name!==`image`||r!==null)return;let i=String(e.attrs.src??``),a=n.get(i)??0;a>0?n.set(i,a-1):r=t}),r}function ae(e,t,n,r){let i=t.chain().focus();switch(e){case`bold`:i.toggleBold().run();break;case`italic`:i.toggleItalic().run();break;case`underline`:i.toggleUnderline().run();break;case`strike`:i.toggleStrike().run();break;case`bullet`:i.toggleBulletList().run();break;case`ordered`:i.toggleOrderedList().run();break;case`indent`:S(t,1);break;case`outdent`:S(t,-1);break;case`image`:n.click();break;case`toc`:r.hidden=!r.hidden;break;case`undo`:i.undo().run();break;case`redo`:i.redo().run()}}function S(e,t){let{$from:n}=e.state.selection;if(n.parent.type.name===`listItem`){let n=e.chain().focus();t>0?n.sinkListItem(`listItem`).run():n.liftListItem(`listItem`).run();return}let r=n.parent.type.name;if(r!==`paragraph`&&r!==`heading`)return;let i=Number(e.getAttributes(r).indent??0);e.chain().focus().updateAttributes(r,{indent:Math.max(0,Math.min(8,i+t))}).run()}var C={avif:`image/avif`,bmp:`image/bmp`,gif:`image/gif`,ico:`image/x-icon`,jpeg:`image/jpeg`,jpg:`image/jpeg`,png:`image/png`,svg:`image/svg+xml`,tif:`image/tiff`,tiff:`image/tiff`,webp:`image/webp`};async function oe(e,t={}){let n=new _.default,r=new DOMParser().parseFromString(e,`text/html`);E(r);let i=ce(t.filename??t.title??`Документ`),a=t.title??r.title??i,o=t.assets??{},s=new Set(Object.keys(o).map(O)),c=0;for(let e of Array.from(r.querySelectorAll(`img[src]`))){let t=e.getAttribute(`src`)?.trim();if(!t||/^https?:\/\//i.test(t)||/^data:(?!image\/)/i.test(t))continue;if(/^data:image\//i.test(t)||/^blob:/i.test(t)){try{let r=await fetch(t);if(!r.ok)throw Error(`Не удалось прочитать изображение: ${t}`);let i=await r.blob();if(!i.type.startsWith(`image/`))throw Error(`Вложение с data/blob URL не является изображением`);let a=`image-${++c}.${A(i.type,t)}`;for(;s.has(a);)a=`image-${++c}.${A(i.type,t)}`;n.file(a,i),s.add(a),e.setAttribute(`src`,encodeURIComponent(a))}catch(e){throw Error(`Не удалось добавить изображение в архив ophx`,{cause:e})}continue}let r=O(t);if(!D(r))throw Error(`Путь изображения должен быть простым именем файла: ${t}`);let i=o[r];if(!i&&!s.has(r))throw Error(`Для локального изображения «${r}» не передан файл media`);i&&n.file(r,i),e.setAttribute(`src`,encodeURIComponent(r))}let l=se(t.styles??{});for(let[e,t]of Object.entries(l))n.file(e,t);let u=Object.keys(l).map(e=>`<link rel="stylesheet" href="${M(encodeURIComponent(e))}">`).join(``),d=r.documentElement.lang||`ru`,f=t.bodyContentEditable?` contenteditable="${t.bodyContentEditable}"`:``,p=`<!doctype html>\n<html lang="${M(d)}"><head><meta charset="utf-8"><title>${M(a)}</title>${u}</head><body${f}>${r.body.innerHTML}</body></html>`;return n.file(`${i}.html`,p),n.generateAsync({type:`blob`,mimeType:`application/zip`,compression:`DEFLATE`,compressionOptions:{level:6}})}async function w(e){await k(e);let t;try{t=await _.default.loadAsync(e)}catch(e){throw Error(`Не удалось прочитать ZIP-архив .ophx (шифрование и неподдерживаемые ZIP-режимы не поддерживаются)`,{cause:e})}if(Object.values(t.files).some(e=>e.dir||/[\\/]/.test(e.name)))throw Error(`В текущей версии .ophx все файлы должны находиться плоским списком в корне архива`);let n=Object.values(t.files).filter(e=>!e.dir),r=n.filter(e=>e.name.toLowerCase().endsWith(`.html`));if(r.length!==1)throw r.length===0&&n.some(e=>e.name.toLowerCase().endsWith(`.xml`))?Error(`Обнаружен устаревший XML-вариант ophx; поддерживается только версия v2 с HTML5 внутри ZIP`):Error(`В архиве должен быть ровно один HTML-файл в корне; найдено: ${r.length}`);let i=r[0],a=i.name.slice(0,-5),o=`name`in e&&typeof e.name==`string`?e.name:``;if(/\.ophx$/i.test(o)&&o.replace(/\.ophx$/i,``)!==a)throw Error(`Имя HTML-файла «${i.name}» должно совпадать с именем архива «${o}»`);let s=await i.async(`string`);if(!/^\s*<!doctype\s+html\b/i.test(s))throw Error(`Основной файл .ophx должен начинаться с <!doctype html>`);let c=new DOMParser().parseFromString(s,`text/html`);if(c.querySelector(`head meta[charset]`)?.getAttribute(`charset`)?.toLowerCase()!==`utf-8`)throw Error(`В <head> документа требуется <meta charset="utf-8">`);let l={};for(let e of Array.from(c.querySelectorAll(`head link[rel~="stylesheet"][href]`))){let n=e.getAttribute(`href`)?.trim();if(!n)continue;if(le(n))throw Error(`CSS должен быть встроенным плоским файлом архива: ${n}`);let r=O(n);if(!D(r)||!r.toLowerCase().endsWith(`.css`))throw Error(`Ссылка на стиль должна указывать на плоский .css-файл: ${n}`);let i=t.file(r);if(!i)throw Error(`Файл стиля «${r}» отсутствует в архиве`);l[r]=await i.async(`string`)}for(let e of Array.from(c.querySelectorAll(`img[src]`))){let n=e.getAttribute(`src`)?.trim();if(!n||le(n))continue;let r=O(n);if(!D(r))throw Error(`Ссылка на изображение должна быть простым именем файла: ${n}`);let i=t.file(r);if(!i)throw Error(`Файл изображения «${r}» отсутствует в архиве`);let a=await i.async(`base64`),o=r.split(`.`).pop()?.toLowerCase()??``,s=C[o]??(o?`image/${o}`:`application/octet-stream`);e.setAttribute(`src`,`data:${s};base64,${a}`)}let u=c.body.getAttribute(`contenteditable`);if(u===`false`)for(let e of Array.from(c.body.children))e.hasAttribute(`contenteditable`)||e.setAttribute(`contenteditable`,`false`);j(c);let d=Object.entries(l);return{html:c.body.innerHTML,title:c.title||a,filename:a,styles:l,css:d[0]?.[1]??``,bodyContentEditable:u===`true`||u===`false`?u:void 0}}function T(e,t){let n=URL.createObjectURL(e),r=window.document.createElement(`a`);r.href=n,r.download=t,r.click(),window.setTimeout(()=>URL.revokeObjectURL(n),0)}function se(e){let t={};for(let[n,r]of Object.entries(e)){let e=O(n);if(!D(e)||!e.toLowerCase().endsWith(`.css`))throw Error(`Имя файла стиля должно быть плоским и оканчиваться на .css: ${n}`);if(Object.hasOwn(t,e))throw Error(`Повторяется имя файла стиля: ${e}`);t[e]=r}return t}function E(e){let t=Array.from(e.querySelectorAll(`[data-ophx-field], .ophx-field`)),n=new Set;for(let e of t){let t=e.getAttribute(`data-ophx-field`)??``,r=e.tagName.toLowerCase();if(!e.classList.contains(`ophx-field`)||e.getAttribute(`contenteditable`)!==`true`)throw Error(`Каждое поле должно иметь класс ophx-field и contenteditable="true"`);if(![`span`,`div`,`p`].includes(r)||!/^[A-Za-z0-9._-]+$/.test(t))throw Error(`Некорректная обёртка или идентификатор поля: ${t||`(пусто)`}`);if(n.has(t))throw Error(`Идентификатор поля должен быть уникальным: ${t}`);if(e.parentElement?.closest(`[data-ophx-field]`))throw Error(`Поля не могут быть вложенными: ${t}`);n.add(t)}}function ce(e){let t=e.trim().replace(/\.(?:ophx|html)$/i,``);if(!t)return`Документ`;if(!D(t)||/[\u0000-\u001f]/.test(t))throw Error(`Имя .ophx должно быть простым именем файла без каталогов`);return t}function D(e){return!!e&&e!==`.`&&e!==`..`&&!/[\\/]/.test(e)}function O(e){try{return decodeURIComponent(e)}catch{throw Error(`Некорректное percent-экранирование в имени ресурса: ${e}`)}}function le(e){return/^(?:data:|https?:\/\/)/i.test(e)}async function k(e){if(e.size<2)throw Error(`Файл не является ZIP-архивом .ophx (возможен устаревший формат)`);let t=new Uint8Array(await e.slice(0,4).arrayBuffer());if(t[0]!==80||t[1]!==75)throw Error(`Неподдерживаемый устаревший ophx: ожидается ZIP-архив версии v2`)}function A(e,t){let n=Object.entries(C).find(([,t])=>t===e)?.[0];if(n)return n;let r=t.match(/\.([a-z0-9]+)(?:[?#]|$)/i)?.[1]?.toLowerCase();return r&&/^[a-z0-9]{1,6}$/.test(r)?r:`bin`}function j(e){e.querySelectorAll(`script, iframe, object, embed, style, link`).forEach(e=>e.remove()),e.querySelectorAll(`*`).forEach(e=>{for(let t of Array.from(e.attributes))/^on/i.test(t.name)&&e.removeAttribute(t.name),/^(href|src|xlink:href)$/i.test(t.name)&&/^\s*(javascript|vbscript):/i.test(t.value)&&e.removeAttribute(t.name),/^(src|xlink:href)$/i.test(t.name)&&/^\s*data:(?!image\/)/i.test(t.value)&&e.removeAttribute(t.name)})}function M(e){return e.replace(/[&<>"']/g,e=>({"&":`&`,"<":`<`,">":`>`,'"':`"`,"'":`'`})[e])}var ue=c.Mark.create({name:`label`,inclusive:!1,addAttributes(){return{label:{default:`highlight`,parseHTML:e=>e.dataset.label??`highlight`,renderHTML:e=>({"data-label":e.label})}}},parseHTML(){return[{tag:`mark[data-label]`},{tag:`mark`}]},renderHTML({HTMLAttributes:e}){return[`mark`,e,0]}});function N(){return{fieldId:{default:``,parseHTML:e=>e.getAttribute(`data-ophx-field`)??``,renderHTML:e=>({"data-ophx-field":e.fieldId??``})},fieldLabel:{default:null,parseHTML:e=>e.getAttribute(`data-ophx-label`),renderHTML:e=>e.fieldLabel?{"data-ophx-label":e.fieldLabel}:{}},required:{default:!1,parseHTML:e=>e.getAttribute(`data-ophx-required`)===`true`,renderHTML:e=>e.required?{"data-ophx-required":`true`}:{}}}}var de=/^[A-Za-z0-9._-]+$/,P=50,F=1200;function I(e){if(!e||!/^\d+$/.test(e))return null;let t=Number(e);return Number.isInteger(t)&&t>=P&&t<=F?t:null}var L=y.default.extend({addAttributes(){return{...this.parent?.(),width:{default:null,parseHTML:e=>I(e.getAttribute(`width`)),renderHTML:e=>{let t=e.width;return Number.isInteger(t)&&Number(t)>=P&&Number(t)<=F?{width:String(t)}:{}}},textWrap:{default:`none`,parseHTML:e=>{let t=e.getAttribute(`data-ophx-wrap`);return t===`left`||t===`right`?t:`none`},renderHTML:e=>({"data-ophx-wrap":e.textWrap===`left`||e.textWrap===`right`?e.textWrap:`none`})},alignment:{default:`left`,parseHTML:e=>{let t=e.getAttribute(`data-ophx-align`);return t===`center`||t===`right`?t:`left`},renderHTML:e=>({"data-ophx-align":e.alignment===`center`||e.alignment===`right`?e.alignment:`left`})}}},addNodeView(){return({node:e,getPos:t,editor:n})=>{let r=e,i=0,a=0,o=0,s=document.createElement(`div`);s.className=`ophx-image-node`,s.contentEditable=`false`;let c=document.createElement(`img`),l=document.createElement(`button`);l.type=`button`,l.className=`ophx-image-node__resize-handle`,l.setAttribute(`role`,`slider`),l.setAttribute(`aria-label`,`Изменить размер изображения перетаскиванием`),l.setAttribute(`aria-valuemin`,String(P)),l.setAttribute(`aria-valuemax`,String(F)),l.title=`Потяните влево или вправо, чтобы изменить размер`,l.innerHTML=`<svg viewBox="0 0 12 28" aria-hidden="true"><path d="M4 3v22M8 3v22"/></svg>`,s.append(c,l);let u=()=>{let e=String(r.attrs.src??``);c.getAttribute(`src`)!==e&&c.setAttribute(`src`,e),c.alt=r.attrs.alt??``,r.attrs.title?c.title=r.attrs.title:c.removeAttribute(`title`),r.attrs.width?c.width=Number(r.attrs.width):c.removeAttribute(`width`);let t=Number(r.attrs.width)||Math.round(c.getBoundingClientRect().width);l.setAttribute(`aria-valuenow`,String(t)),l.setAttribute(`aria-valuetext`,`${t} пикселей`),s.dataset.ophxWrap=r.attrs.textWrap??`none`,s.dataset.ophxAlign=r.attrs.alignment??`left`},d=e=>{if(!n.isEditable)return;let r=typeof t==`function`?t():void 0;if(typeof r!=`number`)return;let i=n.state.doc.nodeAt(r);if(!i||i.type.name!==`image`)return;let a=getComputedStyle(n.view.dom),o=Number.parseFloat(a.paddingLeft)+Number.parseFloat(a.paddingRight),s=Math.max(P,n.view.dom.clientWidth-o),c=Math.min(e,F,Math.floor(s));n.view.dispatch(n.state.tr.setNodeMarkup(r,void 0,{...i.attrs,width:c}))},f=e=>{if(!l.hasPointerCapture(e.pointerId))return;let t=Math.max(50,Math.round((a+e.clientX-i)/5)*5);t!==o&&(o=t,d(t))},p=e=>{l.hasPointerCapture(e.pointerId)&&l.releasePointerCapture(e.pointerId)};return l.addEventListener(`pointerdown`,e=>{n.isEditable&&(e.preventDefault(),e.stopPropagation(),i=e.clientX,a=c.getBoundingClientRect().width,o=Math.round(a),l.setPointerCapture(e.pointerId))}),l.addEventListener(`pointermove`,f),l.addEventListener(`pointerup`,p),l.addEventListener(`pointercancel`,p),l.addEventListener(`keydown`,e=>{if(!n.isEditable||e.key!==`ArrowLeft`&&e.key!==`ArrowRight`)return;e.preventDefault();let t=Number(r.attrs.width)||Math.round(c.getBoundingClientRect().width),i=e.key===`ArrowRight`?10:-10;d(Math.max(P,t+i))}),c.addEventListener(`load`,()=>{if(r.attrs.width)return;let e=Math.round(c.getBoundingClientRect().width);l.setAttribute(`aria-valuenow`,String(e)),l.setAttribute(`aria-valuetext`,`${e} пикселей`)}),u(),{dom:s,update(e){return e.type===r.type&&(r=e,u(),!0)},stopEvent(e){return l.contains(e.target)},ignoreMutation(){return!0}}}}}),R=c.Node.create({name:`ophxInlineField`,priority:1e3,group:`inline`,inline:!0,content:`text*`,defining:!0,addAttributes:N,parseHTML(){return[{tag:`span.ophx-field[contenteditable="true"][data-ophx-field]`,priority:1e3}]},renderHTML({HTMLAttributes:e}){return[`span`,{...e,class:`ophx-field`,contenteditable:`true`},0]}}),z=c.Node.create({name:`ophxBlockField`,priority:1e3,group:`block`,content:`text*`,defining:!0,addAttributes(){return{...N(),tagName:{default:`div`,parseHTML:e=>e.tagName.toLowerCase(),renderHTML:()=>({})}}},parseHTML(){return[{tag:`p.ophx-field[contenteditable="true"][data-ophx-field]`,priority:1e3},{tag:`div.ophx-field[contenteditable="true"][data-ophx-field]`,priority:1e3}]},renderHTML({node:e,HTMLAttributes:t}){return[e.attrs.tagName===`p`?`p`:`div`,{...t,class:`ophx-field`,contenteditable:`true`},0]}}),B=c.Mark.create({name:`ophxReadonlyInline`,inclusive:!1,addAttributes(){return{className:{default:null,parseHTML:e=>e.getAttribute(`class`),renderHTML:e=>e.className?{class:e.className}:{}}}},parseHTML(){return[{tag:`span[contenteditable="false"]`,priority:100}]},renderHTML({HTMLAttributes:e}){return[`span`,{...e,contenteditable:`false`},0]}}),V=c.Node.create({name:`ophxReadonlyBlock`,priority:1e3,group:`block`,content:`block*`,defining:!0,isolating:!0,addAttributes(){return{id:{default:null,parseHTML:e=>e.getAttribute(`id`),renderHTML:e=>e.id?{id:e.id}:{}},className:{default:null,parseHTML:e=>e.getAttribute(`class`),renderHTML:e=>e.className?{class:e.className}:{}},title:{default:null,parseHTML:e=>e.getAttribute(`title`),renderHTML:e=>e.title?{title:e.title}:{}}}},parseHTML(){return[{tag:`div[contenteditable="false"]`,priority:1e3}]},renderHTML({HTMLAttributes:e}){return[`div`,{...e,contenteditable:`false`},0]}}),H=new b.PluginKey(`ophxHeadingCollapse`),U=v.default.extend({addAttributes(){return{...this.parent?.(),collapsed:{default:!1,parseHTML:e=>e.dataset.collapsed===`true`,renderHTML:e=>e.collapsed?{"data-collapsed":`true`}:{}}}},addNodeView(){return({node:e,getPos:t,editor:n})=>{let r=e,i=document.createElement(`h${e.attrs.level}`);i.className=`ophx-heading`;let a=document.createElement(`button`);a.type=`button`,a.className=`ophx-heading__toggle`,a.innerHTML=`<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m5.5 3.5 4.5 4.5-4.5 4.5"/></svg>`;let o=document.createElement(`span`);o.className=`ophx-heading__content`,i.append(o,a);let s=()=>{let e=!!r.attrs.collapsed;a.setAttribute(`aria-expanded`,String(!e)),a.setAttribute(`aria-label`,e?`Развернуть раздел`:`Свернуть раздел`),a.title=e?`Развернуть раздел`:`Свернуть раздел`,i.dataset.collapsed=String(e)};return a.addEventListener(`mousedown`,e=>e.preventDefault()),a.addEventListener(`click`,e=>{if(e.preventDefault(),e.stopPropagation(),!n.isEditable)return;let r=typeof t==`function`?t():void 0;if(typeof r!=`number`)return;let i=n.state.doc.nodeAt(r);i&&i.type.name===`heading`&&n.view.dispatch(n.state.tr.setNodeMarkup(r,void 0,{...i.attrs,collapsed:!i.attrs.collapsed}))}),s(),{dom:i,contentDOM:o,update(e){return e.type!==r.type||e.attrs.level!==r.attrs.level?!1:(r=e,s(),!0)},stopEvent(e){return a.contains(e.target)}}}},addProseMirrorPlugins(){return[new b.Plugin({key:H,props:{decorations(e){let t=[],n=null;return e.doc.forEach((e,r)=>{if(e.type.name===`heading`){let i=Number(e.attrs.level);n!==null&&i<=n&&(n=null),n!==null&&t.push(x.Decoration.node(r,r+e.nodeSize,{class:`ophx-collapsed-content`})),e.attrs.collapsed&&(n=i)}else n!==null&&t.push(x.Decoration.node(r,r+e.nodeSize,{class:`ophx-collapsed-content`}))}),x.DecorationSet.create(e.doc,t)}}})]}}),W=new b.PluginKey(`ophxTextSearch`),G=c.Extension.create({name:`ophxSearch`,addStorage(){return{query:``}},addCommands(){return{setSearchQuery:e=>({tr:t,dispatch:n})=>(this.storage.query=e,n?.(t.setMeta(W,e)),!0)}},addProseMirrorPlugins(){let e=this;return[new b.Plugin({key:W,state:{init:(t,n)=>K(n.doc,e.storage.query),apply:(t,n,r,i)=>{let a=t.getMeta(W);return a!==void 0&&(e.storage.query=a),!t.docChanged&&a===void 0?n:K(i.doc,e.storage.query)}},props:{decorations(e){return W.getState(e)}}})]}});function K(e,t){if(!t.trim())return x.DecorationSet.empty;let n=[],r=t.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`),i=new RegExp(r,`gi`);return e.descendants((e,t)=>{if(e.isText&&e.text){i.lastIndex=0;for(let r of e.text.matchAll(i))r.index!==void 0&&n.push(x.Decoration.inline(t+r.index,t+r.index+r[0].length,{class:`ophx-search-match`}))}}),x.DecorationSet.create(e,n)}var fe=c.Extension.create({name:`ophxWysiwymRules`,addProseMirrorPlugins(){return[new b.Plugin({props:{handleTextInput(e,t,n,r){return r===` `&&/\s/.test(e.state.doc.textBetween(Math.max(0,t-1),t))},handleKeyDown(e,t){if(t.key!==`Enter`||t.shiftKey)return!1;let{$from:n}=e.state.selection;if(!n.parent.isTextblock||n.parent.content.size!==0||n.depth===0)return!1;let r=n.index(n.depth-1),i=n.node(n.depth-1);return[i.maybeChild(r-1),i.maybeChild(r+1)].some(e=>e?.isTextblock&&e.content.size===0)}}})]}}),pe=c.Extension.create({name:`ophxIndentation`,addGlobalAttributes(){return[{types:[`paragraph`,`heading`],attributes:{indent:{default:0,parseHTML:e=>Number(e.dataset.indent??0),renderHTML:e=>e.indent?{"data-indent":String(e.indent)}:{}}}}]}}),me=c.Extension.create({name:`ophxEditability`,addGlobalAttributes(){return[{types:[`paragraph`,`heading`,`blockquote`,`codeBlock`,`bulletList`,`orderedList`,`listItem`,`horizontalRule`,`image`,`table`,`tableRow`,`tableHeader`,`tableCell`,`bold`,`italic`,`underline`,`strike`,`link`,`label`],attributes:{contenteditable:{default:null,parseHTML:e=>{let t=e.getAttribute(`contenteditable`);return t===`true`||t===`false`?t:null},renderHTML:e=>e.contenteditable?{contenteditable:e.contenteditable}:{}}}}]}});function q(e){return de.test(e)}function J(e,t){if(typeof e!=`string`)return e;let n=document.querySelector(e);if(!n)throw Error(`OphxEditor: элемент для опции "${t}" не найден по селектору "${e}"`);return n}var he=`ophx-editor-editable`,ge=class{editor;archiveDefaults=null;editableStorageKey;unmountToolbar=null;constructor(e){this.editableStorageKey=e.editableStorageKey===void 0?he:e.editableStorageKey,this.editor=new c.Editor({element:J(e.element,`element`),content:e.content??`<p></p>`,editable:this.readStoredEditable()??e.editable??!0,extensions:[l.default.configure({heading:!1,underline:!1,link:!1}),U.configure({levels:[1,2,3,4,5,6]}),u.default,d.default.configure({openOnClick:!1,autolink:!0,linkOnPaste:!0}),L.configure({allowBase64:!0,inline:!1}),p.Table.configure({resizable:!0}),m.TableRow,h.TableHeader,g.TableCell,R,z,V,B,ue,pe,me,G,fe,f.default.configure({placeholder:e.placeholder??`Начните писать…`}),...e.extensions??[]],editorProps:{attributes:{class:`ophx-editor-surface`,spellcheck:`true`,lang:`ru`,"aria-label":`Редактор документа`},handleDOMEvents:{mousedown:(e,t)=>{if(e.editable)return!1;let n=t.target instanceof Element?t.target:null;if(!n)return!1;if(n.closest(`.ophx-image-node, img`))return t.preventDefault(),!0;let r=n.closest(`table`),i=n.closest(`p, span, a, strong, em, code`);return r&&(!i||t.metaKey||t.ctrlKey)?(t.preventDefault(),!0):!1}}},onUpdate:e.onUpdate}),this.syncEditableControls(),e.toolbar&&(this.unmountToolbar=te(J(e.toolbar,`toolbar`),this.editor,e.toolbarOptions))}getHTML(){return this.editor.getHTML()}getJSON(){return this.editor.getJSON()}getText(){return this.editor.getText()}get isEmpty(){return this.editor.isEmpty}get isEditable(){return this.editor.isEditable}setContent(e,t={}){this.archiveDefaults=null,this.editor.commands.setContent(e,{emitUpdate:t.emitUpdate??!1})}clearContent(e={}){this.setContent(`<p></p>`,e)}focus(e){return this.editor.commands.focus(e),this}blur(){return this.editor.commands.blur(),this}setEditable(e){return this.editor.setEditable(e,!1),this.writeStoredEditable(e),this.syncEditableControls(),this}readStoredEditable(){if(!this.editableStorageKey)return null;try{let e=localStorage.getItem(this.editableStorageKey);return e===`true`||e!==`false`&&null}catch{return null}}writeStoredEditable(e){if(this.editableStorageKey)try{localStorage.setItem(this.editableStorageKey,String(e))}catch{}}undo(){return this.editor.commands.undo()}redo(){return this.editor.commands.redo()}canUndo(){return this.editor.can().undo()}canRedo(){return this.editor.can().redo()}syncEditableControls(){let e=this.editor.isEditable;this.editor.view.dom.querySelectorAll(`.ophx-heading__toggle, .ophx-image-node__resize-handle`).forEach(t=>{t.disabled=!e}),this.editor.view.dom.dispatchEvent(new Event(`ophx:editablechange`))}async loadOphx(e){let t=await w(e);return this.editor.commands.setContent(t.html,{emitUpdate:!1}),this.archiveDefaults={filename:t.filename,title:t.title,styles:t.styles,bodyContentEditable:t.bodyContentEditable},t}saveOphx(e={}){return oe(this.getHTML(),{...this.archiveDefaults,...e})}async downloadOphx(e={}){let t=e.filename??this.archiveDefaults?.filename??e.title??this.archiveDefaults?.title??`Документ`;T(await this.saveOphx(e),`${t.replace(/\.ophx$/i,``)}.ophx`)}setSearchQuery(e){this.editor.commands.setSearchQuery(e)}getFields(){let e=Array.from(this.editor.view.dom.querySelectorAll(`[data-ophx-field]`)).filter(e=>{let t=e.getAttribute(`data-ophx-field`)??``;return e.classList.contains(`ophx-field`)&&e.getAttribute(`contenteditable`)===`true`&&q(t)}),t=new Map;for(let n of e){let e=n.getAttribute(`data-ophx-field`);t.set(e,(t.get(e)??0)+1)}return e.filter(e=>t.get(e.getAttribute(`data-ophx-field`))===1).map(e=>({id:e.getAttribute(`data-ophx-field`),label:e.getAttribute(`data-ophx-label`),required:e.getAttribute(`data-ophx-required`)===`true`,value:e.textContent??``}))}getMissingRequiredFields(){return this.getFields().filter(e=>e.required&&!e.value.trim())}destroy(){this.unmountToolbar?.(),this.unmountToolbar=null,this.editor.destroy()}};function _e(e,t=`f`){if(!/^[A-Za-z0-9._-]+$/.test(t))throw Error(`Префикс идентификаторов полей содержит недопустимые символы`);let n=new DOMParser().parseFromString(e,`text/html`),r=new Set(Array.from(n.querySelectorAll(`[data-ophx-field]`)).map(e=>e.getAttribute(`data-ophx-field`)??``).filter(Boolean)),i=n.createTreeWalker(n.body,4),a=[];for(;i.nextNode();){let e=i.currentNode,t=e.parentElement;t&&!t.closest(`code, pre, script, style, [contenteditable="false"], [data-ophx-field]`)&&/_{2,}/.test(e.data)&&a.push(e)}let o=[],s=1;for(let e of a){let i=n.createDocumentFragment(),a=0;for(let c of e.data.matchAll(/_{2,}/g)){if(c.index===void 0)continue;c.index>a&&i.append(n.createTextNode(e.data.slice(a,c.index)));let l=`${t}${s++}`;for(;r.has(l);)l=`${t}${s++}`;r.add(l),o.push(l);let u=n.createElement(`span`);u.className=`ophx-field`,u.setAttribute(`contenteditable`,`true`),u.setAttribute(`data-ophx-field`,l),i.append(u),a=c.index+c[0].length}a<e.data.length&&i.append(n.createTextNode(e.data.slice(a))),e.replaceWith(i)}return{html:n.body.innerHTML,count:o.length,ids:o}}exports.CollapsibleHeading=U,exports.Indentation=pe,exports.Label=ue,exports.OphxBlockField=z,exports.OphxEditability=me,exports.OphxEditor=ge,exports.OphxInlineField=R,exports.OphxReadonlyBlock=V,exports.OphxReadonlyInline=B,exports.TextSearch=G,exports.WysiwymRules=fe,exports.convertUnderscoresToFields=_e,exports.downloadBlob=T,exports.exportOphx=oe,exports.importOphx=w,exports.isValidOphxFieldId=q,exports.mountToolbar=te;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.ophx-editor-surface{color:#24262d;overflow-wrap:anywhere;background:#fff;outline:0;min-height:480px;padding:clamp(24px,5vw,64px) clamp(22px,8vw,100px);font:16px/1.75 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;display:flow-root}.ophx-editor-surface>:first-child{margin-top:0}.ophx-editor-surface p{margin:0 0 .8em}.ophx-editor-surface h1,.ophx-editor-surface h2,.ophx-editor-surface h3,.ophx-editor-surface h4,.ophx-editor-surface h5,.ophx-editor-surface h6{color:#191b23;letter-spacing:-.02em;margin:1.2em 0 .45em;scroll-margin-top:24px;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-weight:700;line-height:1.2}.ophx-editor-surface h1{margin-top:.2em;font-size:2.3em}.ophx-editor-surface h2{font-size:1.8em}.ophx-editor-surface h3{font-size:1.45em}.ophx-editor-surface h4{font-size:1.2em}.ophx-editor-surface h5{font-size:1.08em}.ophx-editor-surface h6{font-size:1em}.ophx-editor-surface .ophx-heading{align-items:center;gap:7px;display:flex}.ophx-heading__toggle{color:#b6bdc8;cursor:pointer;background:0 0;border:0;border-radius:5px;flex:0 0 24px;order:2;place-items:center;width:24px;height:24px;margin-left:auto;padding:0;transition:background-color .14s,color .14s;display:inline-grid}.ophx-heading__toggle:hover,.ophx-heading__toggle:focus-visible{color:#1e88c4;background:#eaf3f8;outline:none}.ophx-heading__toggle:disabled{color:#d0d5dc;cursor:default;background:0 0}.ophx-heading__toggle svg{fill:none;stroke:currentColor;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:14px;height:14px;transition:transform .14s}.ophx-heading[data-collapsed=true] .ophx-heading__toggle svg{transform:rotate(0)}.ophx-heading:not([data-collapsed=true]) .ophx-heading__toggle svg{transform:rotate(90deg)}.ophx-heading__content{order:1;min-width:0}.ophx-editor-surface [data-indent="1"]{margin-left:2em}.ophx-editor-surface [data-indent="2"]{margin-left:4em}.ophx-editor-surface [data-indent="3"]{margin-left:6em}.ophx-editor-surface [data-indent="4"]{margin-left:8em}.ophx-editor-surface [data-indent="5"]{margin-left:10em}.ophx-editor-surface [data-indent="6"]{margin-left:12em}.ophx-editor-surface [data-indent="7"]{margin-left:14em}.ophx-editor-surface [data-indent="8"]{margin-left:16em}.ophx-editor-surface ul,.ophx-editor-surface ol{padding-left:1.6em}.ophx-editor-surface a{color:#4359d6;cursor:pointer;text-underline-offset:3px;border-radius:2px;text-decoration:underline;transition:background-color .12s,color .12s,box-shadow .12s}.ophx-editor-surface a:hover{color:#3346bd;background:#edf0ff;text-decoration-thickness:2px;box-shadow:0 0 0 2px #edf0ff}.ophx-editor-surface a:focus-visible{outline-offset:2px;border-radius:2px;outline:2px solid #5868d9}.ophx-editor-surface img{border-radius:6px;max-width:100%;height:auto;display:block}.ophx-editor-surface .ophx-image-node{box-sizing:border-box;width:fit-content;max-width:100%;margin:0 0 .8em;display:table;position:relative}.ophx-editor-surface .ophx-image-node[data-ophx-wrap=left]{float:left;margin:.25em 1em .75em 0}.ophx-editor-surface .ophx-image-node[data-ophx-wrap=right]{float:right;margin:.25em 0 .75em 1em}.ophx-editor-surface .ophx-image-node[data-ophx-wrap=none][data-ophx-align=center]{margin-inline:auto}.ophx-editor-surface .ophx-image-node[data-ophx-wrap=none][data-ophx-align=right]{margin-left:auto}.ophx-editor-surface .ophx-image-node img{border-radius:6px;max-width:100%;height:auto;display:block}.ophx-editor-surface .ophx-image-node.ProseMirror-selectednode{outline-offset:3px;outline:2px solid #5868d9}.ophx-editor-surface[contenteditable=false] .ophx-image-node.ProseMirror-selectednode{outline:0}.ophx-editor-surface[contenteditable=false] .ophx-image-node__resize-handle{display:none!important}.ophx-image-node__resize-handle{z-index:4;color:#fff;cursor:ew-resize;touch-action:none;background:#1e88c4;border:1px solid #fff;border-radius:9px;width:18px;height:34px;padding:3px;display:none;position:absolute;top:50%;right:-9px;transform:translateY(-50%);box-shadow:0 1px 5px #1820304d}.ophx-image-node__resize-handle:hover{background:#082d42}.ophx-image-node.ProseMirror-selectednode .ophx-image-node__resize-handle{place-items:center;display:grid}.ophx-image-node__resize-handle svg{fill:none;stroke:currentColor;stroke-width:1.5px;width:8px;height:22px}.ophx-editor-surface table{border-collapse:collapse;table-layout:fixed;width:100%;margin:1em 0}.ophx-editor-surface th,.ophx-editor-surface td{vertical-align:top;border:1px solid #dfe1e8;padding:8px 10px}.ophx-editor-surface th{background:#f5f6fa;font-weight:650}.ophx-editor-surface mark{background:#fff1a8;border-radius:3px;padding:0 2px}.ophx-editor-surface mark[data-label=важно]{background:#ffd9d9}.ophx-editor-surface mark[data-label=идея]{background:#d9f4e3}.ophx-editor-surface .ophx-field{border-bottom:1px dotted #888;outline:none;min-width:4em}.ophx-editor-surface .ophx-field:focus{border-bottom-style:solid}.ophx-editor-surface .ophx-field:empty:before{content:attr(data-ophx-label);color:#aaa}.ophx-editor-surface .ophx-collapsed-content{display:none}.ophx-editor-surface .ophx-search-match{background:#ffe580;border-radius:2px}.ophx-editor-surface .is-empty:before{content:attr(data-placeholder);color:#a4a8b3;float:left;pointer-events:none;height:0}.ophx-toolbar{z-index:15;isolation:isolate;background:#f8f9fb;border:1px solid #e0e3eb;border-bottom-color:#dfe3ec;border-radius:0;flex-wrap:wrap;align-items:center;gap:6px;padding:10px 12px;font:13px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif;display:flex;position:sticky;top:0;box-shadow:inset 0 1px #ffffffe6}.ophx-toolbar__style-wrap{display:inline-flex;position:relative}.ophx-toolbar__style,.ophx-toolbar__button{color:#2d313b;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;min-height:32px;padding:6px 10px;font:500 13px/1.3 Segoe UI,Tahoma,Geneva,Verdana,sans-serif;transition:background-color .12s,border-color .12s,box-shadow .12s}.ophx-toolbar__style{color:#1f2430;text-align:left;background:#fff;border:1px solid #d8dce6;border-radius:7px;justify-content:space-between;align-items:center;min-width:170px;margin-right:4px;padding:7px 30px 7px 12px;display:inline-flex;position:relative;box-shadow:inset 0 1px #ffffffe6}.ophx-toolbar__style:after{content:"";pointer-events:none;border-bottom:1.5px solid #5d6475;border-right:1.5px solid #5d6475;width:7px;height:7px;position:absolute;top:50%;right:11px;transform:translateY(-60%)rotate(45deg)}.ophx-toolbar__style:focus,.ophx-toolbar__style-option:focus-visible{border-color:#a9b4ff;outline:none;box-shadow:0 0 0 3px #596adc1f}.ophx-toolbar__style-label{color:#1f2430;min-width:0;display:inline-block}.ophx-toolbar__style-menu{z-index:20;background:#fff;border:1px solid #dfe3ee;border-radius:10px;min-width:220px;padding:8px;display:grid;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 8px 24px #1820301a}.ophx-toolbar__style-option{color:#1f2430;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;align-items:center;width:100%;padding:9px 10px;display:flex}.ophx-toolbar__style-option:hover,.ophx-toolbar__style-option.is-selected{background:#f3f6fb;border-color:#e6eaf3}.ophx-toolbar__style-preview{color:#1d2431;width:100%;line-height:1.2;display:block}.ophx-toolbar__style-preview--p{font-size:13px;font-weight:500}.ophx-toolbar__style-preview--h1{letter-spacing:-.03em;font-size:1.9em;font-weight:700}.ophx-toolbar__style-preview--h2{letter-spacing:-.025em;font-size:1.5em;font-weight:700}.ophx-toolbar__style-preview--h3{letter-spacing:-.02em;font-size:1.2em;font-weight:700}.ophx-toolbar__button{color:#2d313b;text-align:center;background:#ffffff59;border-color:#cbd0dccc;min-width:34px;padding:6px 9px;font-weight:500;line-height:1}.ophx-toolbar__button[data-action=bold]{font-weight:700}.ophx-toolbar__button[data-action=italic]{font-style:italic;font-weight:500}.ophx-toolbar__button[data-action=underline]{text-underline-offset:3px;text-decoration:underline}.ophx-toolbar__button[data-action=strike]{text-decoration:line-through}.ophx-toolbar__button[data-action=bullet],.ophx-toolbar__button[data-action=ordered],.ophx-toolbar__button[data-action=outdent],.ophx-toolbar__button[data-action=indent],.ophx-toolbar__button[data-action=image],.ophx-toolbar__button[data-action=undo],.ophx-toolbar__button[data-action=redo]{letter-spacing:0;min-width:38px;font-size:17px;font-weight:500;line-height:1.1}.ophx-toolbar__button svg{stroke:currentColor;fill:none;stroke-width:1.8px;stroke-linecap:round;stroke-linejoin:round;width:16px;height:16px;display:block}.ophx-toolbar__button[data-action=ordered] svg{stroke-width:1.7px}.ophx-toolbar__button[data-action=bullet] svg{width:15px;height:15px}.ophx-toolbar__button[data-action=image]{place-items:center;display:inline-grid}.ophx-toolbar__button[data-action=image] svg{width:17px;height:17px}.ophx-toolbar__button[data-action=link],.ophx-toolbar__button[data-action=table]{place-items:center;min-width:38px;padding-inline:8px;display:inline-grid}.ophx-toolbar__button[data-action=link] svg,.ophx-toolbar__button[data-action=table] svg{width:17px;height:17px}.ophx-toolbar__button[data-action=label],.ophx-toolbar__button[data-action=toc]{letter-spacing:.02em;font-size:12px;font-weight:500}.ophx-toolbar__button:hover,.ophx-toolbar__button.is-active{background:#eef2fb;border-color:#d7dff4}.ophx-toolbar__button:disabled,.ophx-toolbar__style:disabled{opacity:.38;cursor:default}.ophx-toolbar__button:focus-visible,.ophx-toolbar__style:focus-visible{outline-offset:2px;outline:2px solid #1e88c4}.ophx-link-popover{z-index:1000;color:#242733;background:#fff;border:1px solid #dfe3ed;border-radius:10px;width:min(340px,100vw - 24px);padding:14px;font:13px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif;position:fixed;box-shadow:0 10px 28px #1820302e}.ophx-link-popover[hidden]{display:none}.ophx-link-popover__arrow{left:var(--ophx-link-arrow-left,50%);background:#fff;border:1px solid #dfe3ed;width:10px;height:10px;position:absolute;transform:translate(-50%)rotate(45deg)}.ophx-link-popover[data-placement=bottom] .ophx-link-popover__arrow{border-bottom:0;border-right:0;top:-6px}.ophx-link-popover[data-placement=top] .ophx-link-popover__arrow{border-top:0;border-left:0;bottom:-6px}.ophx-link-popover__label{color:#555b69;margin-bottom:7px;font-size:12px;font-weight:600;display:block}.ophx-link-popover__row{gap:8px;display:flex}.ophx-link-popover__input{color:#242733;min-width:0;height:34px;font:inherit;border:1px solid #d8dce6;border-radius:6px;flex:1;padding:0 9px}.ophx-link-popover__actions{align-items:center;gap:8px;margin-top:10px;display:flex}.ophx-link-popover__go,.ophx-link-popover__submit{color:#fff;cursor:pointer;background:#1e88c4;border:1px solid #1e88c4;border-radius:6px;min-height:32px;padding:0 11px;font:600 12px/1 Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ophx-link-popover__go:hover,.ophx-link-popover__submit:hover{color:#fff;background:#082d42;border-color:#082d42}.ophx-link-popover__go[hidden]{display:none}.ophx-link-popover__input:focus{border-color:#7d89df;outline:2px solid #5868d929}.ophx-link-popover__remove{color:#707688;cursor:pointer;background:0 0;border:0;margin-right:auto;padding:2px 0;font:12px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ophx-link-popover__remove:hover{color:#b13d4a}.ophx-link-popover__remove[hidden]{display:none}.ophx-label-popover{width:min(300px,100vw - 24px)}.ophx-label-popover[data-mode=view] .ophx-label-popover__edit,.ophx-label-popover[data-mode=edit] .ophx-label-popover__view,.ophx-label-popover__view-actions[hidden]{display:none}.ophx-label-popover mark,.ophx-label-popover__chip{color:#242733;background:#fff1a8;border-radius:4px;padding:2px 8px}.ophx-label-popover mark[data-label=важно]{background:#ffd9d9}.ophx-label-popover mark[data-label=идея]{background:#d9f4e3}.ophx-label-popover__name{overflow-wrap:anywhere;font-size:15px;font-weight:600}.ophx-label-popover__presets{flex-wrap:wrap;gap:6px;margin-top:10px;display:flex}.ophx-label-popover__presets:empty{display:none}.ophx-label-popover__preset{cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;padding:2px;font:12px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ophx-label-popover__preset:hover{border-color:#d8dce6}.ophx-label-popover__preset.is-selected{border-color:#1e88c4}.ophx-editor-surface mark[data-label]{cursor:pointer}.ophx-image-popover{z-index:1000;color:#242733;background:#fff;border:1px solid #dfe3ed;border-radius:10px;width:min(330px,100vw - 24px);padding:14px;font:13px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif;position:fixed;box-shadow:0 10px 28px #1820302e}.ophx-image-popover[hidden]{display:none}.ophx-image-popover__arrow{left:var(--ophx-image-arrow-left,50%);background:#fff;border:1px solid #dfe3ed;width:10px;height:10px;position:absolute;transform:translate(-50%)rotate(45deg)}.ophx-image-popover[data-placement=bottom] .ophx-image-popover__arrow{border-bottom:0;border-right:0;top:-6px}.ophx-image-popover[data-placement=top] .ophx-image-popover__arrow{border-top:0;border-left:0;bottom:-6px}.ophx-image-popover__heading{color:#252834;margin:0 0 7px;font-size:12px;font-weight:650}.ophx-image-popover__heading:not(:first-of-type){margin-top:12px}.ophx-image-popover__choices{gap:5px;display:flex}.ophx-image-popover__choice{color:#4a5060;cursor:pointer;background:#fff;border:1px solid #dfe3ed;border-radius:6px;place-items:center;width:38px;height:34px;padding:5px;display:grid}.ophx-image-popover__choice:hover{color:#fff;background:#082d42;border-color:#082d42}.ophx-image-popover__choice.is-selected{color:#fff;background:#1e88c4;border-color:#1e88c4}.ophx-image-popover__choice.is-selected:hover{background:#082d42;border-color:#082d42}.ophx-image-popover__choice:focus-visible{outline-offset:2px;outline:2px solid #1e88c4}.ophx-image-popover__choice svg{fill:none;stroke:currentColor;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round;width:25px;height:22px}.ophx-image-popover__size{color:#858b99;margin-top:10px;font-size:11px}.ophx-table-popover{z-index:1000;color:#242733;background:#fff;border:1px solid #dfe3ed;border-radius:10px;grid-template-columns:1fr 1fr;gap:10px;width:min(280px,100vw - 24px);padding:14px;font:13px/1.4 Segoe UI,Tahoma,Geneva,Verdana,sans-serif;display:grid;position:fixed;box-shadow:0 10px 28px #1820302e}.ophx-table-popover[hidden]{display:none}.ophx-table-popover__label{color:#555b69;gap:5px;font-size:12px;font-weight:600;display:grid}.ophx-table-popover__label input{color:#242733;width:100%;height:34px;font:inherit;border:1px solid #d8dce6;border-radius:6px;padding:0 8px}.ophx-table-popover__label input:focus{border-color:#7d89df;outline:2px solid #5868d929}.ophx-table-popover__header{color:#555b69;grid-column:1/-1;align-items:center;gap:7px;font-size:12px;display:flex}.ophx-table-popover__header input{accent-color:#1e88c4}.ophx-table-popover__insert{color:#fff;cursor:pointer;background:#1e88c4;border:1px solid #1e88c4;border-radius:6px;grid-column:1/-1;min-height:34px;font:600 12px/1 Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ophx-table-popover__insert:hover{color:#fff;background:#082d42;border-color:#082d42}.ophx-table-context{z-index:1000;background:#fff;border:1px solid #dfe3ed;border-radius:9px;align-items:center;gap:4px;padding:5px;display:flex;position:fixed;box-shadow:0 8px 24px #18203029}.ophx-table-context[hidden]{display:none}.ophx-table-context button{color:#454b59;cursor:pointer;background:#fff;border:1px solid #0000;border-radius:6px;place-items:center;width:32px;height:30px;padding:5px;display:grid}.ophx-table-context button:hover{color:#fff;background:#082d42;border-color:#082d42}.ophx-table-context button:focus-visible{outline-offset:2px;outline:2px solid #1e88c4}.ophx-table-context button svg{fill:none;stroke:currentColor;stroke-width:1.7px;stroke-linecap:round;stroke-linejoin:round;width:18px;height:18px}.ophx-table-context__divider{background:#e3e6ee;width:1px;height:22px}.ophx-toc{z-index:3;background:#fff;border:1px solid #e1e5ee;border-radius:10px;min-width:220px;max-width:min(360px,90vw);max-height:320px;padding:8px;display:grid;position:absolute;top:calc(100% + 4px);right:12px;overflow:auto;box-shadow:0 10px 24px #16213914}.ophx-toc[hidden]{display:none}.ophx-toc__item{text-align:left;color:#373d4b;cursor:pointer;background:0 0;border:0;border-radius:6px;padding:7px 8px;font:500 13px/1.35 Segoe UI,Tahoma,Geneva,Verdana,sans-serif}.ophx-toc__item:hover{background:#f3f5fa}
|
|
2
|
+
/*$vite$:1*/
|